import sys
import re
import pandas as pd
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, 
                            QHBoxLayout, QPushButton, QTableWidget, QTableWidgetItem,
                            QFileDialog, QMessageBox, QHeaderView, QStatusBar)
from PyQt6.QtCore import QTimer, QThreadPool, QRunnable, pyqtSignal, Qt
import subprocess
import platform
from datetime import datetime
from PyQt6.QtGui import QColor, QBrush

def ping_host(host):
    """
    返回元组 (是否在线, 响应时间)
    """
    param = '-n' if platform.system().lower() == 'windows' else '-c'
    command = ['ping', param, '1', '-w', '1000', host]  # 添加超时参数
    try:
        output = subprocess.check_output(command, universal_newlines=True, timeout=2)
        if platform.system().lower() == 'windows':
            if 'TTL=' in output:
                # Windows下提取响应时间
                for line in output.split('\n'):
                    if 'time=' in line.lower():
                        time_str = line.split('time=')[1].split('ms')[0].strip()
                        # 处理 <1ms 的情况
                        if time_str.startswith('<'):
                            time_str = '0.5'  # 假设 <1ms 为 0.5ms
                        try:
                            return True, float(time_str)
                        except ValueError:
                            return True, 0
                return True, 0
            return False, 0
        else:
            # Linux/Unix系统
            if ' 0% packet loss' in output:
                for line in output.split('\n'):
                    if 'time=' in line:
                        time_str = line.split('time=')[1].split(' ms')[0].strip()
                        # 处理 time<1ms 的情况
                        if time_str.startswith('<'):
                            time_str = '0.5'  # 假设 <1ms 为 0.5ms
                        try:
                            return True, float(time_str)
                        except ValueError:
                            return True, 0
                return True, 0
            return False, 0
    except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
        return False, 0
    except Exception as e:
        print(f"Ping错误 {host}: {str(e)}")  # 调试信息
        return False, 0

def is_valid_ip(ip):
    """验证IP地址格式"""
    pattern = r'^(\d{1,3}\.){3}\d{1,3}$'
    if not re.match(pattern, str(ip)):
        return False
    nums = ip.split('.')
    for num in nums:
        # 去除前导零
        num = str(int(num))  # 将"01"转换为"1"
        if not 0 <= int(num) <= 255:
            return False
    return True

class PingWorker(QRunnable):
    """专门处理ping操作的工作线程"""
    def __init__(self, ip, row, callback):
        super().__init__()
        self.ip = ip
        self.row = row
        self.callback = callback

    def run(self):
        # 尝试ping三次，取最好的结果
        best_result = (False, 0)
        for _ in range(3):
            is_online, response_time = ping_host(self.ip)
            if is_online and (not best_result[0] or (best_result[0] and response_time < best_result[1])):
                best_result = (True, response_time)
            if best_result[0]:  # 如果已经成功就不需要继续尝试
                break
        self.callback(self.row, best_result[0], best_result[1])

class PingView(QMainWindow):
    def __init__(self):
        super().__init__()
        # 设置窗口标题，包含版本信息
        self.setWindowTitle("PingView - 版本 1.0")
        self.setGeometry(100, 100, 1000, 600)

        # 创建中心部件
        central_widget = QWidget()
        self.setCentralWidget(central_widget)

        # 创建布局
        layout = QVBoxLayout(central_widget)

        # 创建按钮布局
        button_layout = QHBoxLayout()

        # 创建按钮
        self.import_btn = QPushButton("导入Excel")
        self.start_btn = QPushButton("开始Ping")
        self.stop_btn = QPushButton("停止")
        self.export_btn = QPushButton("导出结果")

        # 添加按钮到布局
        button_layout.addWidget(self.import_btn)
        button_layout.addWidget(self.start_btn)
        button_layout.addWidget(self.stop_btn)
        button_layout.addWidget(self.export_btn)

        # 创建表格
        self.table = QTableWidget()
        self.table.setColumnCount(7)
        self.table.setHorizontalHeaderLabels(
            ["IP地址", "状态", "响应时间(ms)", "丢包率", "最后更新时间", "描述", "备注"])

        # 设置表格列宽
        header = self.table.horizontalHeader()
        self.table.setColumnWidth(0, 120)  # IP地址列
        self.table.setColumnWidth(1, 80)   # 状态列
        self.table.setColumnWidth(2, 100)  # 响应时间列
        self.table.setColumnWidth(3, 80)   # 丢包率列
        self.table.setColumnWidth(4, 100)  # 最后更新时间列
        self.table.setColumnWidth(5, 200)  # 描述列（设置初始宽度）
        self.table.setColumnWidth(6, 150)  # 备注列（设置初始宽度）

        # 设置列宽调整模式为 Interactive，允许用户手动调整
        header.setSectionResizeMode(5, QHeaderView.ResizeMode.Interactive)  # 描述列
        header.setSectionResizeMode(6, QHeaderView.ResizeMode.Interactive)  # 备注列

        # 添加所有组件到主布局
        layout.addLayout(button_layout)
        layout.addWidget(self.table)

        # 创建状态栏并显示版权信息
        self.status_bar = QStatusBar()
        self.setStatusBar(self.status_bar)
        self.status_bar.showMessage("甘肃传德源科技工程有限公司 版权所有")

        # 连接信号
        self.import_btn.clicked.connect(self.import_excel)
        self.start_btn.clicked.connect(self.start_ping)
        self.stop_btn.clicked.connect(self.stop_ping)
        self.export_btn.clicked.connect(self.export_results)

        # 初始化定时器
        self.timer = QTimer()
        self.timer.timeout.connect(self.ping_all)

        # 存储IP列表、备注和描述
        self.ip_list = []
        self.remarks = []
        self.descriptions = []

        # 创建线程池
        self.thread_pool = QThreadPool()

    def import_excel(self):
        file_name, _ = QFileDialog.getOpenFileName(self, "选择Excel文件", "", "Excel Files (*.xlsx *.xls)")
        if file_name:
            try:
                df = pd.read_excel(file_name, dtype=str)

                if df.empty:
                    raise ValueError("Excel文件为空")

                # 查找所需列
                columns = {
                    'ip': ['ip', 'ip地址'],
                    'desc': ['描述'],
                    'remark': ['备注']
                }

                found_columns = {}
                for col in df.columns:
                    col_name = str(col).lower().replace(' ', '')
                    for key, keywords in columns.items():
                        if any(keyword in col_name for keyword in keywords):
                            found_columns[key] = col
                            break

                if 'ip' not in found_columns:
                    raise ValueError("未找到IP地址列")

                # 处理数据
                valid_data = []
                for i, row in df.iterrows():
                    ip = str(row[found_columns['ip']]).strip()
                    if pd.isna(ip) or not is_valid_ip(ip):
                        continue

                    desc = str(row[found_columns['desc']]) if 'desc' in found_columns else ""
                    remark = str(row[found_columns['remark']]) if 'remark' in found_columns else ""

                    if pd.isna(desc): desc = ""
                    if pd.isna(remark): remark = ""

                    valid_data.append((ip, desc, remark))

                if not valid_data:
                    raise ValueError("没有找到有效的IP地址")

                # 更新数据
                self.ip_list = [x[0] for x in valid_data]
                self.descriptions = [x[1] for x in valid_data]
                self.remarks = [x[2] for x in valid_data]

                self.update_table()
                QMessageBox.information(self, "成功", f"成功导入{len(self.ip_list)}个IP地址")

            except Exception as e:
                import traceback
                print("错误详情:", traceback.format_exc())
                QMessageBox.critical(self, "错误", f"导入失败：\n{str(e)}")

    def update_table(self):
        try:
            self.table.setRowCount(len(self.ip_list))
            for row, (ip, remark, desc) in enumerate(zip(self.ip_list, self.remarks, self.descriptions)):
                # IP地址
                self.table.setItem(row, 0, QTableWidgetItem(str(ip)))
                # 初始化其他列
                for col in range(1, 5):
                    self.table.setItem(row, col, QTableWidgetItem(""))
                # 描述和备注
                self.table.setItem(row, 5, QTableWidgetItem(str(desc)))
                self.table.setItem(row, 6, QTableWidgetItem(str(remark)))

            # 调整列宽
            self.table.resizeColumnsToContents()

        except Exception as e:
            print(f"更新表格错误: {str(e)}")
            QMessageBox.critical(self, "错误", f"更新表格失败: {str(e)}")

    def start_ping(self):
        if not self.ip_list:
            QMessageBox.warning(self, "警告", "请先导入IP地址！")
            return

        # 启动定时器，每秒更新一次
        self.timer.start(1000)

    def stop_ping(self):
        self.timer.stop()
        self.thread_pool.clear()  # 清空线程池

    def ping_all(self):
        # 为每个IP创建新的工作线程
        for row, ip in enumerate(self.ip_list):
            worker = PingWorker(ip, row, self.update_ping_result)
            self.thread_pool.start(worker)

    def update_ping_result(self, row, is_online, response_time):
        try:
            if 0 <= row < self.table.rowCount():  # 检查行索引是否有效
                if is_online:
                    status = "在线"
                    response_time = round(float(response_time), 2)
                    packet_loss = "0%"
                    status_item = QTableWidgetItem(status)
                    status_item.setForeground(QBrush(QColor("#008000")))  # 深绿色
                    status_item.setBackground(QBrush(QColor("#E8F5E9")))  # 淡绿色背景
                else:
                    status = "离线"
                    response_time = "-"
                    packet_loss = "100%"
                    status_item = QTableWidgetItem(status)
                    status_item.setForeground(QBrush(QColor("#D32F2F")))  # 深红色
                    status_item.setBackground(QBrush(QColor("#FFEBEE")))  # 淡红色背景

                self.table.setItem(row, 1, status_item)
                self.table.setItem(row, 2, QTableWidgetItem(str(response_time)))
                self.table.setItem(row, 3, QTableWidgetItem(packet_loss))
                self.table.setItem(row, 4, QTableWidgetItem(datetime.now().strftime("%H:%M:%S")))
        except Exception as e:
            print(f"更新状态错误: {str(e)}")

    def export_results(self):
        if self.table.rowCount() == 0:
            QMessageBox.warning(self, "警告", "没有数据可供导出！")
            return

        file_name, _ = QFileDialog.getSaveFileName(self, "保存结果", "", "Excel Files (*.xlsx)")
        if file_name:
            data = []
            headers = ["IP地址", "状态", "响应时间(ms)", "丢包率", "最后更新时间", "备注", "描述"]
            for row in range(self.table.rowCount()):
                row_data = []
                for col in range(self.table.columnCount()):
                    item = self.table.item(row, col)
                    row_data.append(item.text() if item else "")  # 检查单元格是否为空
                data.append(row_data)

            df = pd.DataFrame(data, columns=headers)
            df.to_excel(file_name, index=False)
            QMessageBox.information(self, "成功", "导出成功！")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = PingView()
    window.show()
    sys.exit(app.exec())